// 上下左右相邻的不能放，放最后一行的时候也只和上一行有关，因此仍然可以用动态规划
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 15, M = 1 << 12, mod = 1e8;
// f[i][j]表示当前已经放到第i行，并且第i行的玉米摆放的状态为j的总方案数
// 属性:Count
int f[N][M];
int g[N];
vector<int> status;
// 注意head数组存储的是下标
vector<int> head[M];
int n, m;

bool check(int i)
{
    return !(i & (i << 1));
}

void init()
{
    // 先预处理出所有可行的状态，即没有连续个1;
    for (int i = 0; i < 1 << m; ++i)
        // 只有是合法状态才需要插入
        if (check(i))
            status.push_back(i);
    // 然后判断每种状态能够由哪些状态转换而来
    for (int i = 0; i < status.size(); ++i)
        for (int j = 0; j < status.size(); ++j)
            // 判断条件：两个状态上下没有同位置的1
            if ((status[i] & status[j]) == 0)
            {
                head[i].push_back(j);
                // cout << i << ' ' << j << endl;
            }
}

int main()
{
    cin >> n >> m;
    init();
    for (int i = 1; i <= n; ++i)
        for (int j = 0; j < m; ++j)
        {
            int tmp;
            cin >> tmp;
            // 如果tmp是0，说明当前位置不能放玉米，将对应位标志成1便于之后的操作
            if (!tmp)
                g[i] += (1 << j);
        }
    // 初始情况下，放了0行，第0行没有玉米的方案数量是1
    f[0][0] = 1;
    // 直接遍历到n+1行就不用计算总和了
    for (int i = 1; i <= n + 1; ++i)
        for (int j = 0; j < status.size(); ++j)
        {
            int st = status[j];
            // 如果当前状态和g[i]的与不为0的话，说明当前状态下有玉米种在了不能种的地方，所以当前状态无效，直接跳过
            if (st & g[i])
                continue;
            for (auto k : head[j])
            {
                // cout << i << ' ' <<  k << ' '<< status[k] << endl;
                // cout << f[i - 1][status[k]] << endl;
                f[i][st] = (f[i][st] + f[i - 1][status[k]]) % mod;
                // cout << "f[i][st] " << f[i][st] << endl;
            }
        }
    cout << f[n + 1][0] << endl;
    return 0;
}
